Fix #765: Upgrade 17 decision models to optimization (Group A + B)#771
Fix #765: Upgrade 17 decision models to optimization (Group A + B)#771
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Min<usize>) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…::Sum>) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…<i64>) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…to optimization (Min<i64>) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… (Min<i64>) + ILP rewrite Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…<f64>) + ILP rewrite Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…<i64>) + ILP rewrite Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Max<usize>) + ILP rewrite + config redesign Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n (Min<usize>) + config redesign Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove bound parameter from 8 model constructor calls in CLI create - Remove associated bound parsing/validation code and CLI tests - Fix SumOfSquaresPartition canonical example (optimal is 226, not 230) - Fix StackerCrane example-db test assertion (no longer has bound field) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…(Min<W::Sum>) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ion (Min<N::Sum>) + ILP rewrite Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…28>) + ILP rewrite Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Sum>) + minimax ILP rewrite Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… (Max<usize>) + config redesign Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ptimization-765 # Conflicts: # docs/paper/reductions.typ
… merge - Convert 10 ILP rules from feasibility to optimization (remove bound, add objective) - Fix SCS ILP: add contiguous padding constraint, fix alphabet_size field - Mark OLA ILP<i32> tests as ignored (ILP solver too slow for integer variables) - Fix sequencing ILP test to use ILPSolver instead of brute-force on ILP<i32> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nces - model_specs_are_optimal now tries brute-force first for instances with search space ≤ 2^20, avoiding HiGHS hangs on ILP<i32> reductions - Hardcode OLA and Sequencing ILP canonical example solutions (avoid calling ILPSolver at example-db build time for ILP<i32> targets) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: ILP<i32> variables default to domain [0, 2^31], causing HiGHS to hang on even tiny instances. Two fixes: 1. ILP solver: derive tighter per-variable upper bounds from single-variable ≤ constraints before passing to HiGHS (covers x and p variables) 2. OLA ILP: add z_e ≤ n-1 bound (max position difference) 3. Sequencing ILP: add z ≤ Σ|costs| bound (max cumulative cost) Restores ILPSolver calls in canonical rule examples (removes hardcoded solutions) and reverts model_specs_are_optimal to ILP-first strategy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lver test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #771 +/- ##
==========================================
- Coverage 97.82% 97.81% -0.01%
==========================================
Files 588 588
Lines 66422 66239 -183
==========================================
- Hits 64976 64793 -183
Misses 1446 1446 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Finding: ILP solver hangs due to unbounded variable domainsDuring this PR, we discovered that Root causeThe ILP solver sets variable upper bounds from v = v.max((V::DIMS_PER_VAR - 1) as f64); // i32 → 2,147,483,647For This particularly affects auxiliary variables that lack explicit single-variable
Fix (3 parts)
ImpactBefore fix: Recommendation for future ILP rulesAlways add explicit |
…verage - Remove #[ignore] from OLA ILP tests (HiGHS is fast now with z bounds) - Add rural_postman tests: wrong config length, is_weighted, solver aggregate - Add SWCP test: weight bound exceeded - Covers codecov gaps in rural_postman, shortest_weight_constrained_path, and optimallineararrangement_ilp Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR upgrades a large set of models that were previously encoded as decision problems (Value = Or + threshold) into true optimization models (Value = Min/Max<T>), and updates the associated ILP reductions, CLI creation/help text, canonical examples, and tests so that solvers produce and validate optimal objective values.
Changes:
- Convert 17 models from thresholded decision formulations to optimization (
Min/Max) and updateevaluate()to return objective values (Some(v)/None). - Rewrite multiple
→ ILPreductions from feasibility to optimization by removing bound constraints and adding objective functions (including minimax encodings where needed). - Update CLI help/tests/docs and unit/integration tests to reflect new constructors, config encodings (padding/slots), and optimization semantics.
Reviewed changes
Copilot reviewed 79 out of 79 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/suites/integration.rs | Updates integration assertions to check Value presence for optimization models. |
| src/unit_tests/trait_consistency.rs | Updates constructors in trait consistency tests after removing threshold params. |
| src/unit_tests/solvers/customized/solver.rs | Adjusts customized-solver tests for new optimization semantics. |
| src/unit_tests/rules/sumofsquarespartition_ilp.rs | Updates SSP ILP tests to compare optimal values (BF vs ILP). |
| src/unit_tests/rules/stackercrane_ilp.rs | Switches round-trip helper to optimization variant; updates constructor. |
| src/unit_tests/rules/shortestweightconstrainedpath_ilp.rs | Updates SWCP ILP tests to optimization objective and Min values. |
| src/unit_tests/rules/shortestcommonsupersequence_ilp.rs | Updates SCS ILP tests for padding-based optimization objective. |
| src/unit_tests/rules/sequencingtominimizemaximumcumulativecost_ilp.rs | Updates STMMCC ILP tests to compare optimal objective values. |
| src/unit_tests/rules/ruralpostman_ilp.rs | Updates RPP ILP tests for optimization and adds BF-vs-ILP optimality check. |
| src/unit_tests/rules/optimallineararrangement_ilp.rs | Updates OLA ILP tests to optimization and adds BF-vs-ILP optimality check. |
| src/unit_tests/rules/multiplecopyfileallocation_ilp.rs | Updates MCFA ILP tests to optimization and removes budget constraint checks. |
| src/unit_tests/rules/mixedchinesepostman_ilp.rs | Updates MCPP ILP tests to optimization; adds BF-vs-ILP comparisons. |
| src/unit_tests/rules/minmaxmulticenter_ilp.rs | Updates MMC ILP tests to ILP<i32> minimax + objective variable z. |
| src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs | Updates MinCutBS ILP tests to optimization round-trip helper and Some checks. |
| src/unit_tests/rules/longestcommonsubsequence_ilp.rs | Updates LCS ILP tests for padding encoding and BF-vs-ILP optimality checks. |
| src/unit_tests/rules/longestcircuit_ilp.rs | Updates LongestCircuit ILP tests to maximization objective and optimization helper. |
| src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs | Updates LBDP ILP tests to optimization round-trip helper (maximize #paths). |
| src/unit_tests/rules/expectedretrievalcost_ilp.rs | Updates ERC ILP tests to objective-based optimization and cost comparisons. |
| src/unit_tests/rules/capacityassignment_ilp.rs | Updates CA ILP tests to optimize cost subject to delay budget. |
| src/unit_tests/models/misc/sequencing_to_minimize_maximum_cumulative_cost.rs | Updates STMMCC model tests to Min(Some(v))/Min(None) semantics. |
| src/unit_tests/models/misc/expected_retrieval_cost.rs | Updates ERC model tests to return objective values (no threshold). |
| src/unit_tests/models/misc/capacity_assignment.rs | Updates CA model tests to Min<u128> objective and feasibility via delay budget. |
| src/unit_tests/models/graph/shortest_weight_constrained_path.rs | Updates SWCP model tests to Min objective and witness optimality assertions. |
| src/unit_tests/models/graph/multiple_copy_file_allocation.rs | Updates MCFA model tests to optimization objective and removes threshold logic. |
| src/unit_tests/models/graph/mixed_chinese_postman.rs | Updates MCPP model tests for optimization objective values. |
| src/unit_tests/models/graph/longest_circuit.rs | Updates LongestCircuit model tests for maximization objective values. |
| src/unit_tests/example_db.rs | Updates example-db assertions to reflect removed bound fields / new optimal values. |
| src/solvers/ilp/solver.rs | Tightens per-variable upper bounds using single-var <= constraints to improve ILP performance. |
| src/solvers/customized/solver.rs | Updates MinimumCardinalityKey customized backend to remove bound-based pruning. |
| src/rules/sumofsquarespartition_ilp.rs | Converts SSP ILP reduction from feasibility to minimization objective. |
| src/rules/stackercrane_ilp.rs | Converts StackerCrane ILP reduction from bound constraint to objective minimization. |
| src/rules/shortestweightconstrainedpath_ilp.rs | Converts SWCP ILP reduction to minimize length subject to weight constraint. |
| src/rules/shortestcommonsupersequence_ilp.rs | Implements padding + contiguous-padding constraints and objective for SCS minimization via maximizing padding. |
| src/rules/sequencingtominimizemaximumcumulativecost_ilp.rs | Converts STMMCC reduction to minimax ILP with explicit z objective variable. |
| src/rules/ruralpostman_ilp.rs | Converts RPP reduction from length-bound feasibility to objective minimization. |
| src/rules/optimallineararrangement_ilp.rs | Converts OLA reduction from bound feasibility to objective minimization. |
| src/rules/multiplecopyfileallocation_ilp.rs | Converts MCFA reduction to objective minimization and redefines Big-M for unreachable pairs. |
| src/rules/mixedchinesepostman_ilp.rs | Converts MCPP reduction from bound feasibility to objective minimization. |
| src/rules/minmaxmulticenter_ilp.rs | Converts MMC reduction to minimax ILP<i32> with binary bounds + z objective. |
| src/rules/minimumcutintoboundedsets_ilp.rs | Converts MinCutBS reduction to objective minimization (removes cut bound). |
| src/rules/longestcircuit_ilp.rs | Converts LongestCircuit ILP reduction to maximization objective (removes length threshold). |
| src/rules/lengthboundeddisjointpaths_ilp.rs | Redesigns LBDP ILP reduction to maximize number of active path slots with activation vars. |
| src/rules/expectedretrievalcost_ilp.rs | Converts ERC reduction from bound feasibility to objective minimization. |
| src/rules/capacityassignment_ilp.rs | Converts CA reduction to objective minimization (cost) subject to delay constraint. |
| src/models/set/minimum_cardinality_key.rs | Converts MinimumCardinalityKey to Min<i64> objective and removes bound from schema/struct. |
| src/models/misc/sum_of_squares_partition.rs | Converts SSP model to Min<i64> objective and removes bound field/serialization. |
| src/models/misc/stacker_crane.rs | Converts StackerCrane model to Min<i32> objective and removes bound. |
| src/models/misc/sequencing_to_minimize_maximum_cumulative_cost.rs | Converts STMMCC model to Min<i64> objective and removes bound. |
| src/models/misc/mod.rs | Updates misc model list entry for StackerCrane to reflect optimization semantics. |
| src/models/misc/longest_common_subsequence.rs | Converts LCS to padding-based fixed-length encoding and Max<usize> objective. |
| src/models/misc/expected_retrieval_cost.rs | Converts ERC to Min<f64> objective and removes bound. |
| src/models/misc/capacity_assignment.rs | Converts CA to Min<u128> objective and removes cost budget. |
| src/models/graph/optimal_linear_arrangement.rs | Converts OLA to Min<usize> objective and removes bound. |
| src/models/graph/multiple_copy_file_allocation.rs | Converts MCFA to Min<i64> objective and removes bound. |
| src/models/graph/mixed_chinese_postman.rs | Converts MCPP to Min<W::Sum> objective and removes bound. |
| src/models/graph/minimum_cut_into_bounded_sets.rs | Converts MinCutBS to Min<W::Sum> objective and removes cut bound. |
| src/models/graph/min_max_multicenter.rs | Converts MMC to Min<W::Sum> objective and removes bound. |
| src/models/graph/longest_circuit.rs | Converts LongestCircuit to Max<W::Sum> objective and removes bound. |
| problemreductions-cli/src/mcp/tests.rs | Updates MCP test payloads to reflect removed bound parameter(s). |
| problemreductions-cli/src/cli.rs | Updates CLI “flags by problem type” + args docs for removed parameters. |
| docs/src/cli.md | Updates docs output/examples for renamed fields and updated parameterization. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/solvers/customized/solver.rs
Outdated
| // Search for any minimal key (the smallest one will be found first due to | ||
| // branch-and-bound ordering in the FD subset search) | ||
| let result = fd_subset_search::search_fd_subset( | ||
| n, | ||
| &essential, |
There was a problem hiding this comment.
solve_minimum_cardinality_key currently searches for any minimal key (subset-minimal superkey) and returns the first one found. search_fd_subset does DFS that tries including each attribute first, so the first minimal key found is biased toward larger keys and is not guaranteed to be the minimum-cardinality key required by the optimization objective. Please change the search strategy to guarantee minimum size (e.g., iterative deepening by cardinality with pruning, or change the DFS order to explore smaller subsets first while maintaining minimality checks), and add an assertion in tests that the customized solver’s returned witness matches the brute-force optimum cardinality/value.
problemreductions-cli/src/cli.rs
Outdated
| MinimumCutIntoBoundedSets --graph, --edge-weights, --source, --sink, --size-bound, --cut-bound | ||
| MinimumCutIntoBoundedSets --graph, --edge-weights, --source, --sink, --size-bound | ||
| HamiltonianCircuit, HC --graph | ||
| LongestCircuit --graph, --edge-weights, --bound |
There was a problem hiding this comment.
The CLI help still lists LongestCircuit as requiring --bound, but the model (and create implementation) has been upgraded to optimization and no longer has a bound parameter. Update the flags-by-problem-type list accordingly so users aren’t instructed to pass an unused/invalid flag.
| LongestCircuit --graph, --edge-weights, --bound | |
| LongestCircuit --graph, --edge-weights |
problemreductions-cli/src/cli.rs
Outdated
| #[arg(long)] | ||
| pub required_edges: Option<String>, | ||
| /// Bound parameter (lower bound for LongestCircuit; upper or length bound for BoundedComponentSpanningForest, GroupingBySwapping, LengthBoundedDisjointPaths, LongestCommonSubsequence, MultipleCopyFileAllocation, MultipleChoiceBranching, OptimalLinearArrangement, RootedTreeArrangement, RuralPostman, ShortestCommonSupersequence, or StringToStringCorrection) | ||
| /// Bound parameter (lower bound for LongestCircuit; upper or length bound for BoundedComponentSpanningForest, GroupingBySwapping, LengthBoundedDisjointPaths, LongestCommonSubsequence, MultipleCopyFileAllocation, MultipleChoiceBranching, RootedTreeArrangement, RuralPostman, or StringToStringCorrection) |
There was a problem hiding this comment.
The --bound flag docstring still mentions models that no longer take a bound (e.g., LongestCircuit, LongestCommonSubsequence, MultipleCopyFileAllocation). This makes pred create help misleading after the optimization upgrade; please remove those models from this description (and consider deleting the flag entirely if nothing uses it anymore).
| /// Bound parameter (lower bound for LongestCircuit; upper or length bound for BoundedComponentSpanningForest, GroupingBySwapping, LengthBoundedDisjointPaths, LongestCommonSubsequence, MultipleCopyFileAllocation, MultipleChoiceBranching, RootedTreeArrangement, RuralPostman, or StringToStringCorrection) | |
| /// Bound parameter (upper or length bound for BoundedComponentSpanningForest, GroupingBySwapping, LengthBoundedDisjointPaths, MultipleChoiceBranching, RootedTreeArrangement, RuralPostman, or StringToStringCorrection) |
docs/src/cli.md
Outdated
| pred create MaxCut --graph 0-1,1-2,2-0 -o maxcut.json | ||
| pred create MinMaxMulticenter --graph 0-1,1-2,2-3 --weights 1,1,1,1 --edge-weights 1,1,1 --k 2 --bound 1 -o pcenter.json | ||
| pred create MinMaxMulticenter --graph 0-1,1-2,2-3 --weights 1,1,1,1 --edge-weights 1,1,1 --k 2 -o pcenter.json | ||
| pred create ShortestWeightConstrainedPath --graph 0-1,0-2,1-3,2-3,2-4,3-5,4-5,1-4 --edge-lengths 2,4,3,1,5,4,2,6 --edge-weights 5,1,2,3,2,3,1,1 --source-vertex 0 --target-vertex 5 --length-bound 10 --weight-bound 8 -o swcp.json |
There was a problem hiding this comment.
The SWCP example still uses --length-bound, but the CLI flags list above (and the model upgrade) removed that parameter. This example should be updated to remove --length-bound (and keep only --weight-bound) so it remains runnable.
| pred create ShortestWeightConstrainedPath --graph 0-1,0-2,1-3,2-3,2-4,3-5,4-5,1-4 --edge-lengths 2,4,3,1,5,4,2,6 --edge-weights 5,1,2,3,2,3,1,1 --source-vertex 0 --target-vertex 5 --length-bound 10 --weight-bound 8 -o swcp.json | |
| pred create ShortestWeightConstrainedPath --graph 0-1,0-2,1-3,2-3,2-4,3-5,4-5,1-4 --edge-lengths 2,4,3,1,5,4,2,6 --edge-weights 5,1,2,3,2,3,1,1 --source-vertex 0 --target-vertex 5 --weight-bound 8 -o swcp.json |
| pub fn new(alphabet_size: usize, strings: Vec<Vec<usize>>) -> Self { | ||
| let max_length = strings.iter().map(|s| s.len()).min().unwrap_or(0); | ||
| assert!( | ||
| max_length >= 1 || strings.is_empty(), | ||
| "at least one string must be non-empty" | ||
| ); |
There was a problem hiding this comment.
LongestCommonSubsequence::new computes max_length as the minimum string length, but then asserts that at least one string must be non-empty. This panics for valid instances where any input string is empty (the correct optimum is length 0), and also for the case where all strings are empty. Please allow max_length == 0 and handle it consistently (e.g., allow an empty config/dims, or special-case to a 0-length padded encoding) instead of panicking.
Agentic Review ReportStructural CheckStructural Review: Generic (17 Model Upgrades + ILP Rule Rewrites)Build Status
Blacklisted Files
Semantic Review — Models Sampled (6)
Semantic Review — ILP Rules Sampled (4)
Mathematical Correctness
Issue Compliance (#765)All 17 models correctly upgraded:
Issues Found
Quality CheckQuality ReviewDesign Principles
HCI (CLI changed)
Test Quality
IssuesCritical (Must Fix)
Important (Should Fix)
Minor (Nice to Have)
Agentic Feature TestsTest Environment
Results
* Pre-existing issue: Details
Issues Found
Generated by review-pipeline |
- Fix CustomizedSolver for MinimumCardinalityKey to guarantee minimum cardinality using iterative deepening by subset size, not just any minimal key (critical correctness fix for optimization upgrade) - Remove stale --bound references from CLI help and usage strings for LongestCircuit, MixedChinesePostman, RuralPostman, StackerCrane, LCS, SCS, MinimumCardinalityKey, MultipleCopyFileAllocation, and SequencingToMinimizeMaximumCumulativeCost - Remove stale --num-paths-required from LengthBoundedDisjointPaths help - Remove stale --k from MinimumCardinalityKey help - Remove --length-bound from SWCP example in docs/src/cli.md - Fix OLA->ILP overhead num_constraints (add num_edges for z_e bounds) - Fix LongestCircuit->ILP overhead (n-1 commodities, not n) - Allow LCS max_length == 0 for empty input strings instead of panicking - Strengthen LCS brute-force test assertion to check exact optimal value - Add optimality assertions to CustomizedSolver MCK tests - Update cli_tests to reflect removed --bound flags Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix Summary (cd58bdf)Addresses all review comments and agentic review findings from the previous round. Review Comments Addressed
Agentic Review Findings Addressed
Not Addressed (deferred)
Verification
|
zazabap
left a comment
There was a problem hiding this comment.
Reviewed: all review comments addressed, make check passes, fixes verified.
Summary
Value = Or+ threshold to true optimization (Value = Max<T>/Min<T>)createcommands, canonical examples, paper entries, and all testsGroup A — single-threshold models (12)
Max<W::Sum>Min<usize>Min<W::Sum>Min<W::Sum>Min<i32>Min<i64>Min<i64>Min<i64>Min<f64>Min<i64>Max<usize>Min<usize>Group B — constrained-optimization models (5)
size_boundMin<W::Sum>weight_boundMin<N::Sum>delay_budgetMin<u128>k(centers)Min<W::Sum>max_lengthMax<usize>Test plan
make check(fmt + clippy + test) passescargo test --lib --features example-db— 3206 passedcargo test -p problemreductions-cli— 17 passedcargo test --test main(integration + doc tests) — all passedcargo clippy --all-targets— 0 warningsCloses #765
🤖 Generated with Claude Code